package com.google.gwt.sample.simplechat.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.sockets.client.*;
import com.google.gwt.sockets.client.impl.FlashTextSocketImpl;
import com.google.gwt.sockets.client.impl.JavaTextSocketImpl;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.*;

/**
 * The simple chat client for testing Sockets Module for GWT.
 *
 * @author Aleksey Lagoshin
 */
public class ChatClient implements EntryPoint {

  private static ChatClient instance;

  public static ChatClient getInstance() {
    return instance;
  }

  /**
   * Text Socket that allows to send and receive UTF-8 strings followed by a zero byte.
   */
  private TextSocket socket;

  /**
   * Host of the server side.
   */
  private String host = "localhost";
  /**
   * Port of the server side.
   */
  private int port = 777;

  private ScrollPanel scrollPanel;
  private VerticalPanel history = new VerticalPanel();
  private TextBox input = new TextBox();
  private TextBox tbHost = new TextBox();

  /**
   * Inits GUI.
   */
  public void onModuleLoad() {
    instance = this;

    initGUI();
  }

  /**
   * Creates simple GUI with history panel, such buttons like "Connect", "Disconnect", "Send", etc.
   */
  private void initGUI() {
    VerticalPanel mainPanel = new VerticalPanel();
    mainPanel.setBorderWidth(1);
    scrollPanel = new ScrollPanel(history);
    scrollPanel.setSize("400", "300");
    input.setWidth("400");

    tbHost.setWidth("165px");

    HorizontalPanel controlPanel = new HorizontalPanel();
    Button btnSend = new Button("Send");
    btnSend.setWidth("55px");
    Button btnConnect = new Button("Connect");
    btnConnect.setWidth("80px");
    Button btnDisconnect = new Button("Disconnect");
    btnDisconnect.setWidth("100px");
    controlPanel.add(btnSend);
    controlPanel.add(btnConnect);
    controlPanel.add(btnDisconnect);
    controlPanel.add(tbHost);

    tbHost.setText(host + ":" + port);

    input.addKeyboardListener(new KeyboardListenerAdapter() {
      public void onKeyPress(Widget sender, char keyCode, int modifiers) {
        if (keyCode == KeyboardListener.KEY_ENTER)
          sendMessage();
      }
    });

    btnSend.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        sendMessage();
      }
    });

    btnConnect.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        connect();
      }
    });

    btnDisconnect.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        if (socket.isConnected())
          disconnect();
        else
          println(" ** Not connected to the server");
      }
    });

    RadioButton rbAutomatic = new RadioButton("sockets", "Automatic");
    RadioButton rbFlash = new RadioButton("sockets", "Flash");
    RadioButton rbJava = new RadioButton("sockets", "Java");
    rbAutomatic.setChecked(true);
    rbAutomatic.addClickListener(new SocketsSwicth());
    rbJava.addClickListener(new SocketsSwicth());
    rbFlash.addClickListener(new SocketsSwicth());

    FlowPanel socketsPanel = new FlowPanel();
    socketsPanel.add(rbAutomatic);
    socketsPanel.add(rbFlash);
    socketsPanel.add(rbJava);

    mainPanel.add(new Label("Sockets implementation:"));
    mainPanel.add(socketsPanel);
    mainPanel.add(scrollPanel);
    mainPanel.add(input);
    mainPanel.add(controlPanel);

    RootPanel.get("client_slot").add(mainPanel);

    println("This is a chat client, it uses Macromedia Flash or \n" +
            "Java Applet to obtain sockets functionality.\n" +
            "You can manually choose implementation of sockets\n" +
            "by selecting one of radio buttons above. \n" +
            "Use \"Connect\" button to connect to a chat server.");
  }

  public void connect() {
    if (socket != null) try {
      if (socket.isConnected())
        disconnect();
      // Removes a socket object from page
      socket.shutdown();
    }
    catch (SocketException e) {
      println(" ** Exception while shutting down the socket object: " + e.getMessage());
    }

    String[] hostPort = tbHost.getText().split("[:]");

    host = hostPort[0];
    port = Integer.parseInt(hostPort[1]);

    println(" ** Connecting to the server...");

    // Creates socket that client's browser supports
    // For some case Flash can't reload policy file if server was down when Flash made the first
    // request for that file.
    // To avoid issues with loading the policy file for Flash we recreate a socket object.
    socket = SocketsFactory.createTextSocket(new SocketHandler(), "xmlsocket://" + host + ":" + port);
    // Client will start connecting when socket is created and ready to work (see onReady method in
    // SocketHandler class)
  }

  public void disconnect() {
    try {
      socket.disconnect();
      println(" ** Disconnected from the server.");
    }
    catch (IOException e) {
      println(" ** Input/output exception while disconnecting from the server: " + e.getMessage());
    }
  }

  /**
   * Sends a messager to a server.
   */
  public void sendMessage() {
    if (socket.isConnected()) {
      String data = input.getText();
      input.setText("");
      try {
        socket.write(data);
        println(" >> " + data);
      }
      catch (IOException e) {
        println(" ** Exception while sending a message to the server: " + e.getMessage());
      }
    }
    else {
      println(" ** " + "Not connected to the server.");
    }
  }

  public TextSocket getSocket() {
    return socket;
  }

  public void setSocket(TextSocket socket) {
    this.socket = socket;
  }

  public String getHost() {
    return host;
  }

  public int getPort() {
    return port;
  }

  /**
   * Adds a message to the history panel.
   *
   * @param message a message
   */
  public void println(String message) {
    history.add(new Label(message));
    scrollToBottom();
  }

  public void scrollToBottom() {
    scrollToBottomImpl(scrollPanel.getElement());
  }

  private native void scrollToBottomImpl(Element element) /*-{
    element.scrollTop = element.scrollHeight;
  }-*/;

}
